package org.jflame.auth.web.spring;

import org.jflame.auth.LoginUser;
import org.jflame.auth.authz.HasPermit;
import org.jflame.auth.authz.UrlPermit;
import org.jflame.auth.authz.WildcardPermit;
import org.jflame.auth.context.UserContextHolder;
import org.jflame.commons.model.CallResult;
import org.jflame.commons.model.CallResult.ResultEnum;
import org.jflame.commons.util.StringHelper;
import org.jflame.commons.util.UrlHelper;
import org.jflame.commons.util.UrlMatcher;
import org.jflame.web.WebUtils;
import org.jflame.web.spring.SpringWebUtils;
import org.jflame.web.spring.inteceptor.BaseInterceptor;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;

/**
 * 基于url权限判断拦截器
 * 
 * @author charles.zhang
 */
public class AuthorityInteceptor extends BaseInterceptor {

    private String fobidUrl;// 无权限转向页面地址，相对应用路径
    private final CallResult<?> errJson = new CallResult<>(ResultEnum.FORBIDDEN);
    private String superAdminRole = null;
    private Map<String,String[]> roleMatchers = new HashMap<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        String uri = WebUtils.getRequestPath(request);
        if (isIgnoreUrl(uri)) {
            return true;
        }
        if (log.isDebugEnabled()) {
            log.debug("auth filter,uri:{}", uri);
        }
        // 获取用户
        LoginUser curUser = UserContextHolder.getContext()
                .getUser();
        if (curUser == null) {
            return false;
        }
        // 超管
        if (superAdminRole != null && curUser.hasRole(superAdminRole)) {
            return true;
        }

        // 按角色判断权限,如只有admin才能访问/sys/**开头的地址
        if (!roleMatchers.isEmpty()) {
            for (String pattern : roleMatchers.keySet()) {
                if (UrlMatcher.match(pattern, uri)) {
                    for (String r : roleMatchers.get(pattern)) {
                        if (curUser.hasRole(r)) {
                            return true;
                        }
                    }
                }
            }
        }

        if (!curUser.isPermitted(new UrlPermit(uri))) {
            log.warn("access no permission url:{},user:{}", uri, curUser.getUserName());
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            if (SpringWebUtils.isJsonResult(request, handler)) {
                WebUtils.outJson(response, errJson);
            } else {
                if (fobidUrl != null) {
                    if (UrlHelper.isAbsoluteUri(fobidUrl)) {
                        response.sendRedirect(uri);
                    } else {
                        request.getRequestDispatcher(fobidUrl)
                                .forward(request, response);
                    }
                } else {
                    response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "403 forbidden");
                }
            }
            return false;
        }

        return true;
    }

    public static List<WildcardPermit> resolver(Object handler) {
        if (handler != null && handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            HasPermit annot = handlerMethod.getMethodAnnotation(HasPermit.class);
            if (annot == null) {
                annot = handlerMethod.getBeanType()
                        .getAnnotation(HasPermit.class);
            }
            List<WildcardPermit> permits = new ArrayList<>(annot.value().length);
            for (String p : annot.value()) {
                permits.add(new WildcardPermit(p));
            }
            return permits;
        }
        return null;
    }

    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
            throws Exception {
    }

    @Override
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
            throws Exception {
    }

    public void setFobidUrl(String fobidUrl) {
        this.fobidUrl = fobidUrl;
    }

    /**
     * 设置超级管理员的角色标识,如果设置了值表示启用超管角色无需验证权限
     * 
     * @param superAdminRoleCode
     */
    public void setSuperAdminRole(String superAdminRoleCode) {
        this.superAdminRole = StringHelper.trimToNull(superAdminRoleCode);
    }

    /**
     * 增加一个角色匹配的url地址,表示url匹配的地址必须具有参数role任意一个角色才能访问
     * 
     * @param antUrlPattern
     * @param role
     */
    public void addRoleMatcher(String antUrlPattern, String... role) {
        if (role != null) {
            roleMatchers.put(antUrlPattern, role);
        }
    }
}
